package com.sunlands.zlcx.usercenter.controller;

import com.sunlands.zlcx.usercenter.client.LiveVideoService;
import com.sunlands.zlcx.usercenter.common.annotation.AutoLog;
import com.sunlands.zlcx.usercenter.constant.AppEnum;
import com.sunlands.zlcx.usercenter.constant.YN;
import com.sunlands.zlcx.usercenter.domain.VipSetMealDO;
import com.sunlands.zlcx.usercenter.domain.VipUserDO;
import com.sunlands.zlcx.usercenter.domain.VipUserSetMealRecordDO;
import com.sunlands.zlcx.usercenter.exception.GlobalExceptionHandler;
import com.sunlands.zlcx.usercenter.service.UsersServiceImpl;
import com.sunlands.zlcx.usercenter.service.UsersStudyServiceImpl;
import com.sunlands.zlcx.usercenter.service.VipUserServiceImpl;
import com.sunlands.zlcx.usercenter.util.ClientUtil;
import com.sunlands.zlcx.usercenter.util.Version;
import com.sunlands.zlcx.usercenter.vo.*;
import com.sunlands.zlcx.usercenter.vo.response.BusinessResult;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.sunlands.zlcx.usercenter.domain.VipSetMealDO.TypeEnum.*;
import static com.sunlands.zlcx.usercenter.domain.VipUserDO.LevelEnum.STUDY_OPEN_DAY;
import static com.sunlands.zlcx.usercenter.vo.CommonResultMessage.ORDER_NO_EXIST;
import static com.sunlands.zlcx.usercenter.vo.CommonResultMessage.ORDER_NO_REPEAT;

/**
 * @author shixiaopeng
 */
@SuppressWarnings("unused")
@Api(tags = "7、VIP相关", position = 7)
@ApiResponses(value = {@ApiResponse(code = 400, response = GlobalExceptionHandler.class, message = "数据校验失败"), @ApiResponse(code = 500, response = GlobalExceptionHandler.class, message = "内部错误")})
@Slf4j
@RestController
@RequestMapping("/vip")
public class VipUsersController implements AuthSupport {

    @Autowired
    private VipUserServiceImpl vipUserService;
    @Autowired
    UsersStudyServiceImpl usersStudyService;
    @Autowired
    UsersServiceImpl usersService;
    @Autowired
    LiveVideoService liveVideoService;
    @Autowired
    Version version;
    @Resource(name = "vipChargingExecutor")
    private ThreadPoolTaskScheduler vipChargingExecutor;

    @AutoLog
    @ApiOperation(value = "vip会员特权说明--APP调用")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/privilege/list")
    public BusinessResult<List<VipPrivilegeVO>> getVipSetMeal() {
        return BusinessResult.createSuccessInstance(vipUserService.privilege());
    }

    @AutoLog
    @ApiOperation(value = "根据id查价格等信息--服务端调用")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/set-meal/{id}")
    public BusinessResult<VipSetMealDO> getVipSetMeal(@ApiParam(required = true, value = "商品id") @PathVariable Long id) {
        return BusinessResult.createSuccessInstance(vipUserService.findSetMealById(id));
    }

    @AutoLog
    @ApiOperation(value = "获取套餐列表--app调用")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/set-meal/list")
    public BusinessResult<List<VipSetMealDO>> listVipSetMeal(HttpServletRequest request) {
        String os = ClientUtil.getOs(request);

        if (!os.equals("IPhone")) {
            os = "Android";
        }
        Long userId = currentUserId();
        List<VipSetMealDO> vipSetMealDOS = vipUserService.vipSetMealList(userId, os);
        String app = getApp(request);
        if (app.equals(AppEnum.postgraduate.name())) {
            vipSetMealDOS = vipSetMealDOS.stream().filter(vipSetMealDO -> (vipSetMealDO.getType().intValue() != month_card.code.intValue() && vipSetMealDO.getType().intValue() != season_card.code.intValue() && vipSetMealDO.getType().intValue() != year_card.code.intValue())).collect(Collectors.toList());
        }
        return BusinessResult.createSuccessInstance(vipSetMealDOS);
    }

    @AutoLog
    @ApiOperation(value = "根据客户端系统获取卡列表--服务端支付模块调用")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/set-meal/pay/list")
    public BusinessResult<List<VipSetMealDO>> listVipSetMeal(@ApiParam(required = true) @RequestParam String os) {
        final List<VipSetMealDO> setMealDOS = vipUserService.findByOs(os);
        return BusinessResult.createSuccessInstance(setMealDOS);
    }

    @AutoLog
    @ApiOperation(value = "获取连麦时长列表(V3.0.5显示三个进度条:冲刺卡/免费时长/普通vip)--app调用")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/live-times/list")
    public BusinessResult<List<VipUserVO>> getLiveTimesList(HttpServletRequest request) {
        Long userId = currentUserId();
        LocalDateTime today = LocalDateTime.now();
        List<VipUserVO> vipUsers = VipUserVO.fromDO(vipUserService.allVipUsers(userId));

        //如果不是同桌，则不显示开放日VIP
        String app = getApp(request);
        if (!AppEnum.tongzhuo.name().equals(app) || !vipUserService.isOpenDay()) {
            vipUsers = vipUsers.stream().filter(vip -> !STUDY_OPEN_DAY.code.equals(vip.getLevel())).collect(Collectors.toList());
        }

        List<VipUserVO> userVOS = vipUsers.stream().
                filter(vipUserVO -> vipUserVO.getStatus().intValue() == VipUserDO.StatusEnum.valid.code.intValue()).
                filter(vipUserVO -> {
                    if ((VipUserDO.LevelEnum.FREE_TIMES.code.equals(vipUserVO.getLevel()) || VipUserDO.LevelEnum.HOUR_TIMES.code.equals(vipUserVO.getLevel()))
                            && vipUserVO.getTotalTime() > 0
                            && vipUserVO.getUseredTime().intValue() == vipUserVO.getTotalTime().intValue()) {
                        return false;
                    }
                    return true;
                }).map(vipUserVO -> {
            if (!VipUserDO.LevelEnum.FREE_TIMES.code.equals(vipUserVO.getLevel())
                    && !VipUserDO.LevelEnum.HOUR_TIMES.code.equals(vipUserVO.getLevel())
                    && !VipUserDO.LevelEnum.STUDY_OPEN_DAY.code.equals(vipUserVO.getLevel())) {
                long daysDiff = ChronoUnit.DAYS.between(today, vipUserVO.getCurrExpireDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
                daysDiff++;
                vipUserVO.setCurrRemainDays(daysDiff);
                vipUserVO.setSurplusRecoverySecond((vipUserVO.getCurrExpireDate().getTime() - System.currentTimeMillis()) / 1000);
            }
            return vipUserVO;
        }).collect(Collectors.toList());

        //判断是否添加体验进度条
        boolean flag = true;
        for (VipUserVO vipUserVO : userVOS) {
            if (vipUserVO.getTotalTime() > vipUserVO.getUseredTime()) {
                flag = false;
                break;
            }
        }
        if (flag) {
            LiveExperienceVO liveExperienceVO = liveVideoService.applyPublish(userId).getData();
            VipUserVO experienceVO = VipUserVO.builder().
                    level(VipUserDO.LevelEnum.EXPERIENCE_TIMES.code).
                    userId(userId).
                    status(VipUserDO.StatusEnum.valid.code).
                    totalFreeNum(liveExperienceVO.getTotalFreeNum()).
                    freeNum(liveExperienceVO.getFreeNum()).
                    freeMinutesEveryTime(liveExperienceVO.getFreeTimes()).
                    build();
            userVOS.add(experienceVO);
            List<VipUserVO> userVOList = userVOS.stream()
                    .sorted((o1, o2) -> o2.getLevel().compareTo(o1.getLevel()))
                    .collect(Collectors.toList());
            return BusinessResult.createSuccessInstance(userVOList);
        }
        return BusinessResult.createSuccessInstance(userVOS);
    }

    @AutoLog
    @ApiOperation(value = "获取vip用户信息--app调用/服务端调用")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/info")
    public BusinessResult<VipUserVO> getVipInfo(@ApiParam(required = true) @RequestParam Long userId) {
        VipUserVO vipUserVO = getVipUserVO(userId);
        return BusinessResult.createSuccessInstance(vipUserVO);
    }

    /**
     * v3.4.3之后废弃
     */
    @Deprecated
    private VipUserVO getVipUserVO(@ApiParam(required = true) @RequestParam Long userId) {
        VipUserVO vipUserVO = vipUserService.checkVip(userId);
        if (Objects.nonNull(vipUserVO) && Objects.nonNull(vipUserVO.getExpireDate())) {
            LocalDate expireLocalDate = vipUserVO.getExpireDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            Instant instant = expireLocalDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
            Date date = Date.from(instant);
            vipUserVO.setValidDate(date);

            if (Objects.nonNull(vipUserVO.getCurrExpireDate())) {
                LocalDateTime today = LocalDateTime.now();
                long daysDiff = ChronoUnit.DAYS.between(today, vipUserVO.getCurrExpireDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());

                daysDiff++;
                vipUserVO.setCurrRemainDays(daysDiff);
            }
        }
        return vipUserVO;
    }

    @AutoLog
    @ApiOperation(value = "获取vip用户信息/判断是否能连麦--服务端调用（3.4.3版本后不再使用）")
    @GetMapping("/check")
    public BusinessResult<VipUserVO> checkVip(@ApiParam(required = true) @RequestParam Long userId) {
        return BusinessResult.createSuccessInstance(VipUserVO.fromDO(vipUserService.checkLive(userId)));
    }

    @AutoLog
    @ApiOperation(value = "获取用户是否能连麦--APP调用")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/gather")
    public BusinessResult<LiveGatherVO> gather(@ApiParam(required = true) @RequestParam Long userId, HttpServletRequest request) {
        LiveGatherVO liveGatherVO = new LiveGatherVO();
        liveGatherVO.setIsAllow(false);
        liveGatherVO.setIsCreateRoom(false);
        //开放日
        List<VipUserDO> allVip = vipUserService.allVipUsers(userId);
        allVip.stream()
                .filter(vip -> YN.YES.getCode().equals(vip.getStatus()))
                .filter(vip -> vip.getLevel().equals(STUDY_OPEN_DAY.code))
                .filter(vip -> vipUserService.isOpenDay())
                .filter(vip -> AppEnum.tongzhuo.name().equals(getApp(request)))
                .findFirst()
                .ifPresent(vip -> liveGatherVO.setOpenDay(VipUserVO.fromDO(vip)));

        //VIP
        List<VipUserDO> validVipUsers = vipUserService.validVipFilter(allVip);
        validVipUsers.stream().findFirst().ifPresent(vip -> {
            Date expireDate = validVipUsers.stream().filter(vipUser -> Objects.nonNull(vipUser.getExpireDate())).map(VipUserDO::getExpireDate).max(Date::compareTo).orElse(null);
            vip.setExpireDate(expireDate);
            VipUserVO vipUserVO = VipUserVO.fromDO(vip);
            vipUserVO.setValidDate(expireDate);
            liveGatherVO.setVipUser(vipUserVO);
        });

        //免费时长相关信息
        allVip.stream()
                .filter(vipUser -> VipUserDO.LevelEnum.FREE_TIMES.code.equals(vipUser.getLevel()))
                .filter(vipUser -> vipUser.getUseredTime() < vipUser.getTotalTime())
                .findFirst()
                .ifPresent(vip -> liveGatherVO.setFreeTimesUser(VipUserVO.fromDO(vip)));

        if (Objects.nonNull(liveGatherVO.getOpenDay()) || Objects.nonNull(liveGatherVO.getVipUser()) || Objects.nonNull(liveGatherVO.getFreeTimesUser())) {
            liveGatherVO.setIsCreateRoom(true);
        }

        boolean match = allVip.stream().filter(vip -> YN.YES.getCode().equals(vip.getStatus()))
                .filter(vip -> {
                    if (Objects.isNull(liveGatherVO.getOpenDay())) {
                        return !STUDY_OPEN_DAY.code.equals(vip.getLevel());
                    }
                    return true;
                })
                .anyMatch(vip -> vip.getTotalTime() > vip.getUseredTime());
        if (match) {
            liveGatherVO.setIsAllow(true);
        } else {
            liveGatherVO.setIsAllow(false);
        }

        //体验相关信息--不是VIP且没有没有免费时长,不是学习开放日/vip时长和免费时长都用尽，返回体验
        if (!liveGatherVO.getIsAllow()) {
            LiveExperienceVO liveExperience = liveVideoService.applyPublish(userId).getData();
            liveGatherVO.setLiveExperience(liveExperience);
            if (liveExperience.getFreeNum() > 0) {
                liveGatherVO.setIsAllow(true);
            }
            if (liveExperience.getCreateRoomFlag().equals(YN.YES.getCode()) && liveExperience.getFreeNum() > 0) {
                liveGatherVO.setIsCreateRoom(true);
            }
        }

        return BusinessResult.createSuccessInstance(liveGatherVO);
    }


    @AutoLog
    @ApiOperation(value = "开通/续费vip")
    @PreAuthorize("isAuthenticated()")
    @PostMapping("/open")
    public BusinessResult<VipUserVO> openVip(@RequestBody VipVO vipVO) {
        log.info("开通/续费 vipVO = {}", vipVO);
        VipUserSetMealRecordDO setMealRecordDO = vipUserService.findByOrderNoAndOperateType(vipVO.getOrderNo(), VipUserSetMealRecordDO.OperateEnums.BUY.code);
        if (Objects.nonNull(setMealRecordDO)) {
            return BusinessResult.createInstance(ORDER_NO_REPEAT.code, "订单号" + vipVO.getOrderNo() + "不可重复使用", null);
        }
        VipUserDO vipUserDO = vipUserService.openVip(vipVO);

        //获得免费时长后，异步实时扣费，马上触发扣费切换
        vipChargingExecutor.execute(() -> vipUserService.preReduceTime(vipUserDO.getUserId()));
        return BusinessResult.createSuccessInstance(VipUserVO.fromDO(vipUserDO));
    }

    @AutoLog
    @ApiOperation(value = "关闭vip--退费涉及到")
    @PreAuthorize("isAuthenticated()")
    @PostMapping("/close")
    public BusinessResult<VipUserVO> closeVip(@RequestBody VipVO vipVO) {
        log.info("关闭vip vipVO = {}", vipVO);
        //订单号是否存在
        VipUserSetMealRecordDO setMealRecordDOBuy = vipUserService.findByOrderNoAndOperateType(vipVO.getOrderNo(), VipUserSetMealRecordDO.OperateEnums.BUY.code);
        if (Objects.isNull(setMealRecordDOBuy)) {
            return BusinessResult.createInstance(ORDER_NO_EXIST.code, vipVO.getOrderNo() + ORDER_NO_EXIST.message, null);
        }
        VipUserSetMealRecordDO setMealRecordDO = vipUserService.findByOrderNoAndOperateType(vipVO.getOrderNo(), VipUserSetMealRecordDO.OperateEnums.REFUND.code);
        if (Objects.nonNull(setMealRecordDO)) {
            return BusinessResult.createInstance(ORDER_NO_REPEAT.code, "订单号" + vipVO.getOrderNo() + "不可重复使用", null);
        }
        return BusinessResult.createSuccessInstance(VipUserVO.fromDO(vipUserService.returnVip(vipVO)));
    }

}
